#pragma once
#include "../source/server.hpp"
#include <fstream>
#include <ctype.h>
#include <sys/stat.h>
#include <regex>

#define DEFAULT_TIMEOUT 10

// HTTP响应状态码对应的描述信息
std::unordered_map<int, std::string> _statu_info = {
    {100, "Continue"},
    {101, "Switching Protocol"},
    {102, "Processing"},
    {103, "Early Hints"},
    {200, "OK"},
    {201, "Created"},
    {202, "Accepted"},
    {203, "Non-Authoritative Information"},
    {204, "No Content"},
    {205, "Reset Content"},
    {206, "Partial Content"},
    {207, "Multi-Status"},
    {208, "Already Reported"},
    {226, "IM Used"},
    {300, "Multiple Choice"},
    {301, "Moved Permanently"},
    {302, "Found"},
    {303, "See Other"},
    {304, "Not Modified"},
    {305, "Use Proxy"},
    {306, "unused"},
    {307, "Temporary Redirect"},
    {308, "Permanent Redirect"},
    {400, "Bad Request"},
    {401, "Unauthorized"},
    {402, "Payment Required"},
    {403, "Forbidden"},
    {404, "Not Found"},
    {405, "Method Not Allowed"},
    {406, "Not Acceptable"},
    {407, "Proxy Authentication Required"},
    {408, "Request Timeout"},
    {409, "Conflict"},
    {410, "Gone"},
    {411, "Length Required"},
    {412, "Precondition Failed"},
    {413, "Payload Too Large"},
    {414, "URI Too Long"},
    {415, "Unsupported Media Type"},
    {416, "Range Not Satisfiable"},
    {417, "Expectation Failed"},
    {418, "I'm a teapot"},
    {421, "Misdirected Request"},
    {422, "Unprocessable Entity"},
    {423, "Locked"},
    {424, "Failed Dependency"},
    {425, "Too Early"},
    {426, "Upgrade Required"},
    {428, "Precondition Required"},
    {429, "Too Many Requests"},
    {431, "Request Header Fields Too Large"},
    {451, "Unavailable For Legal Reasons"},
    {501, "Not Implemented"},
    {502, "Bad Gateway"},
    {503, "Service Unavailable"},
    {504, "Gateway Timeout"},
    {505, "HTTP Version Not Supported"},
    {506, "Variant Also Negotiates"},
    {507, "Insufficient Storage"},
    {508, "Loop Detected"},
    {510, "Not Extended"},
    {511, "Network Authentication Required"}};

// 文件拓展名对应的MIME名称
std::unordered_map<std::string, std::string> _mime_info = {
    {".aac", "audio/aac"},
    {".abw", "application/x-abiword"},
    {".arc", "application/x-freearc"},
    {".avi", "video/x-msvideo"},
    {".azw", "application/vnd.amazon.ebook"},
    {".bin", "application/octet-stream"},
    {".bmp", "image/bmp"},
    {".bz", "application/x-bzip"},
    {".bz2", "application/x-bzip2"},
    {".csh", "application/x-csh"},
    {".css", "text/css"},
    {".csv", "text/csv"},
    {".doc", "application/msword"},
    {".docx", "application/vnd.openxmlformats-officedocument.wordprocessingml.document"},
    {".eot", "application/vnd.ms-fontobject"},
    {".epub", "application/epub+zip"},
    {".gif", "image/gif"},
    {".htm", "text/html"},
    {".html", "text/html"},
    {".ico", "image/vnd.microsoft.icon"},
    {".ics", "text/calendar"},
    {".jar", "application/java-archive"},
    {".jpeg", "image/jpeg"},
    {".jpg", "image/jpeg"},
    {".js", "text/javascript"},
    {".json", "application/json"},
    {".jsonld", "application/ld+json"},
    {".mid", "audio/midi"},
    {".midi", "audio/x-midi"},
    {".mjs", "text/javascript"},
    {".mp3", "audio/mpeg"},
    {".mpeg", "video/mpeg"},
    {".mpkg", "application/vnd.apple.installer+xml"},
    {".odp", "application/vnd.oasis.opendocument.presentation"},
    {".ods", "application/vnd.oasis.opendocument.spreadsheet"},
    {".odt", "application/vnd.oasis.opendocument.text"},
    {".oga", "audio/ogg"},
    {".ogv", "video/ogg"},
    {".ogx", "application/ogg"},
    {".otf", "font/otf"},
    {".png", "image/png"},
    {".pdf", "application/pdf"},
    {".ppt", "application/vnd.ms-powerpoint"},
    {".pptx", "application/vnd.openxmlformats-officedocument.presentationml.presentation"},
    {".rar", "application/x-rar-compressed"},
    {".rtf", "application/rtf"},
    {".sh", "application/x-sh"},
    {".svg", "image/svg+xml"},
    {".swf", "application/x-shockwave-flash"},
    {".tar", "application/x-tar"},
    {".tif", "image/tiff"},
    {".tiff", "image/tiff"},
    {".ttf", "font/ttf"},
    {".txt", "text/plain"},
    {".vsd", "application/vnd.visio"},
    {".wav", "audio/wav"},
    {".weba", "audio/webm"},
    {".webm", "video/webm"},
    {".webp", "image/webp"},
    {".woff", "font/woff"},
    {".woff2", "font/woff2"},
    {".xhtml", "application/xhtml+xml"},
    {".xls", "application/vnd.ms-excel"},
    {".xlsx", "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"},
    {".xml", "application/xml"},
    {".xul", "application/vnd.mozilla.xul+xml"},
    {".zip", "application/zip"},
    {".3gp", "video/3gpp"},
    {".3g2", "video/3gpp2"},
    {".7z", "application/x-7z-compressed"}};

// Util模块提供一些功能性接口，用于更好的解析和处理HTTP格式数据的请求行，请求报头，以及文件操作，获取文件类型等等
// 读取文件内容
// 向文件写入内容
// URL编码
// URL解码
// 通过HTTP状态码获取描述信息
// 通过文件后缀名获取mime
// 判断一个文件是不是目录
// 判断一个文件是否是一个普通文件
// HTTP资源路径有效性判断
class Util
{
public:
    // 字符串分割，将src字符串按照sep字符进行分割，得到的各个字串放到strs中
    static void Split(const std::string &src, const std::string &sep, std::vector<std::string> *strs)
    {
        size_t cur = 0;
        size_t pos = 0;
        // src: 222<<333<<555<<
        // sep: <<
        while (cur < src.size() && pos < src.size())
        {
            // 从当前位置查找sep分隔符
            pos = src.find(sep, cur);
            if (pos != cur)
            {
                // 找到了，所以截取
                strs->push_back(src.substr(cur, pos - cur));
                cur = pos + sep.size();
            }
            else
            {
                cur++;
            }
        }
    }

    // 读取服务器上的文件里的所有内容，将读取到的内容放到buf里面
    static bool ReadFile(const std::string &filename, std::string *buf)
    {
        // 为了保证数据读取上来后完全一致，我们采用二进制的读取和解析方式，在解析比特位的时候，不会按照ascll码8个比特位为一个字节的方式进行解析，而是使用二进制的方式来进行解析
        // 一般对于文本文件来说都是使用定长编码方式的方式，例如ASCII编码、UNICODE编码，ASCII编码采用8个特定的比特来表示每一个字符，而UNICODE编码采用16比特

        /*对于二进制文件来说，使用的是自定义编码，也就是不定长度的编码，由你来决定多少个比特表示一个什么值，在以二进制方式来读取文件时
        解码的方式同样为不定长度的解码，也就是说二进制编码写入文件的时候是怎样定义的，那么二进制解码读取文件的时候就以同样的方式进行解析*/

        // 以二进制形式打开文件
        std::ifstream ifs(filename.c_str(), std::ios::binary);

        if (ifs.is_open() == false)
        {
            ELOG("open: %s file failed!!!", filename.c_str());
            return false;
        }

        // 获取文件大小
        size_t fsize = 0;
        ifs.seekg(0, std::ios::end); // 跳转读写位置到文件末尾
        fsize = ifs.tellg();         // 从末尾偏移量获取当前流操作的位置，这个下标刚好就是文件的大小
        ifs.seekg(0, std::ios::beg); // 重新将文件流操作的位置移动到开始位置，然后开始进行文件的读取操作

        // 读取文件内容
        buf->resize(fsize);
        ifs.read(&((*buf)[0]), fsize);

        // 判断是否读取成功
        if (ifs.good() == false)
        {
            ELOG("read %s content failed!!!", filename.c_str());
            return false;
        }

        // 关闭文件
        ifs.close();
        return true;
    }

    // 向服务器上写入文件内容，客户端想修改服务器上的某个文件内容，或者想向服务器内部写入一些文件
    static bool WriteFile(const std::string &filename, const std::string &buf)
    {
        // 以二进制形式打开文件，并清空原文件内容
        std::ofstream ofs(filename.c_str(), std::ios::binary | std::ios::trunc);
        if (ofs.is_open() == false)
        {
            ELOG("open: %s file failed!!!", filename.c_str());
            return false;
        }

        // 向文件中写入数据
        ofs.write(buf.c_str(), buf.size());

        // 判断是否写入成功
        if (ofs.good() == false)
        {
            ELOG("write %s content failed!!!", filename.c_str());
            return false;
        }

        // 关闭文件
        ofs.close();
        return true;
    }

    /* URL编码: 避免URL中的请求资源路径和查询字符串中的特殊字符与HTTP请求中的特殊含义字符发生冲突，而导致产生歧义
    编码格式，将特殊字符的ascll码值，转换为两个十六进制字符，前缀为%
    RFC3986文档规定，URL中只允许包含英文字母（a-zA-Z）、数字（0-9）、-_.~4个特殊字符以及所有保留字符: # $ % ^ & _ { } ~ \
    如果URL中包含了其他字符，例如空格、引号、中文等，那么可能会造成服务器或者浏览器的解析错误，或者导致URL的语义变化，所以我们需要进行编码
    W3C文档规定，查询字符串parameter中的空格，需要被转换为+号，请求资源路径并没有要求
    注意: url编码指的是我们提交的数据如果含有和url中的特殊字符的话，那此时就需要进行url编码，而url本身method /path?key=val&key=val version
    所存在的特殊字符? = & /并不会被进行编码，会原封不动的在url里面存在，编码的是我们自己提交的数据。 */

    static std::string UrlEncode(const std::string &url, bool convert_space_to_plus)
    {
        std::string res;
        for (auto &c : url)
        {
            if (c == ' ' && convert_space_to_plus)
                res += '+';
            else if (c != '.' && c != '-' && c != '_' && c != '~' && !isalnum(c))
            {
                char tmp[4] = {0};                       //"%HH\0"，总共4个字符，所以开4个char空间大小
                snprintf(tmp, sizeof(tmp), "%%%02X", c); // 两个%号相当于输出一个%
                res += tmp;
            }
            else
                res += c; // 不需要编码: 英文字母（a-zA-Z）、数字（0-9）、-_.~4个特殊字符以及所有保留字符: "# $ % ^ & _ { } ~ \"
        }
        return res;
    }

    // 将两个16进制数转为10进制数，比如20分别传给ch1，ch2，所以第一位就要乘以16(也就是左移4位)
    static char Convert(char ch1, char ch2)
    {
        char num1, num2;
        if (ch1 >= '0' && ch1 <= '9')
            num1 = (ch1 - '0') << 4;
        else if (ch1 >= 'a' && ch1 <= 'z')
            num1 = (ch1 - 'a') << 4;
        else if (ch1 >= 'A' && ch1 <= 'Z')
            num1 = (ch1 - 'A') << 4;

        if (ch2 >= '0' && ch2 <= '9')
            num2 = ch2 - '0';
        else if (ch2 >= 'a' && ch2 <= 'z')
            num2 = ch2 - 'a';
        else if (ch2 >= 'A' && ch2 <= 'Z')
            num2 = ch2 - 'A';

        return num1 + num2;
    }

    static std::string UrlDecode(const std::string &url, bool convert_plus_to_space)
    {
        std::string res;

        for (int i = 0; i < url.size(); i++)
        {
            if (url[i] == '+' && convert_plus_to_space)
                res += ' ';
            else if (url[i] == '%' && (i + 2 < url.size()))
            {
                // 遇到了%，则将紧随其后的两个字符转换为数字，第一个字符所对应的数字左移四位，第二个字符转为数字即可
                res += Convert(url[i + 1], url[i + 2]);
                i += 2;
            }
            else
                res += url[i]; // 不需要解码: 英文字母（a-zA-Z）、数字（0-9）、-_.~4个特殊字符以及所有保留字符: "# $ % ^ & _ { } ~ \"
        }
        return res;
    }

    // 获取响应状态码对应的描述信息
    static std::string StateDesc(int status)
    {
        auto iter = _statu_info.find(status);
        if (iter != _statu_info.end())
            return iter->second;
        return "Unknow";
    }

    // 通过文件后缀名获取Mime互联网上所传输的内容类型
    static std::string MimeByExt(const std::string &filename)
    {
        // a.b.txt  先获取文件扩展名
        size_t pos = filename.find_last_of('.');
        if (pos == std::string::npos)
            return "application/octet-stream"; // 没有找到文件扩展名就返回一个二进制流

        // 根据扩展名，获取mime
        std::string ext = filename.substr(pos); // ".xxx"截取的子串包括.点字符
        auto iter = _mime_info.find(ext);
        if (iter == _mime_info.end())
            return "application/octet-stream"; // 有文件扩展名，但是这个文件扩展名_mime_info中没有存储相应的信息

        return iter->second;
    }

    // 判断一个文件是否是目录
    static bool IsDirectory(const std::string &filename)
    {
        // int stat(const char *path, struct stat *buf);传入一个文件名，获取这个文件的属性
        struct stat st;
        int ret = stat(filename.c_str(), &st);
        if (ret < 0)
            return false;
        return S_ISDIR(st.st_mode);
    }

    // 判断一个文件是否是普通文件
    static bool IsRegular(const std::string &filename)
    {
        // int stat(const char *path, struct stat *buf);传入一个文件名，获取这个文件的属性
        struct stat st;
        int ret = stat(filename.c_str(), &st);
        if (ret < 0)
            return false;
        return S_ISREG(st.st_mode);
    }

    // 判断请求的资源路径是否有效
    static bool IsValidPath(const std::string &reqpath)
    {
        /*
            /index.html前边的/叫做相对根目录，也就是服务器上的某个webroot子目录，客户端只能请求相对根目录下的资源！
            /../login像这样的请求资源路径就是不合理的，也就是无效的请求资源路径
        */
        /*
            webroot根目录深度层次默认设置为1，如果在解析请求资源路径的过程中，level变成了0，那么就说明该请求资源路径一定是无效的
            遇到了".."字符串，level就减1，遇到/"xxx"/两个/之间的字符串，level就加1
        */
        int level = 1;
        std::vector<std::string> subdir;
        Split(reqpath, "/", &subdir);
        for (auto &s : subdir)
        {
            if (s == "..")
                level--;
            else
                level++;
            if (level <= 0)
                return false;
        }
        return true;
    }
};

// 存储HTTP请求的相关要素信息(接收到一个数据按照HTTP请求格式进行解析，得到各个关键要素放在Request中)
/*
要素:
    请求方法 URL 协议版本\r\n
    各种头部字段\r\n
    \r\n
    正文(有可能没有)
*/
class HttpRequest
{
public:
    // 由于外部需要直接对成员变量进行设置和读取，所以这里使用public修饰
    std::string _method;                                   // 请求方法
    std::string _path;                                     // 资源路径
    std::string _version;                                  // 协议版本
    std::string _body;                                     // 请求正文
    std::smatch _matches;                                  // 保存请求行正则解析后的所有子串(保存资源路径正则匹配的结果)
    std::unordered_map<std::string, std::string> _headers; // 请求头部字段
    std::unordered_map<std::string, std::string> _params;  // ?后&字符间隔的查询字符串
public:
    HttpRequest()
        : _version("HTTP/1.1")
    {
    }

    // 添加头部字段
    void SetHeader(const std::string &key, const std::string &val) { _headers.insert(std::make_pair(key, val)); }

    // 重置请求内容
    void Reset()
    {
        _method = _path = _body = "";
        _version = "HTTP/1.1";
        std::smatch empty;
        _matches.swap(empty);
        _headers.clear();
        _params.clear();
    }

    // 查询是否存在指定头部字段
    bool HasHeader(const std::string &key) const
    {
        auto it = _headers.find(key);
        if (it == _headers.end())
            return false;
        return true;
    }

    // 获取指定头部字段的值
    std::string GetHeader(const std::string &key) const
    {
        auto it = _headers.find(key);
        if (it == _headers.end())
            return "";
        return it->second;
    }

    // 插入查询字符串
    void SetParam(const std::string &key, const std::string &val) { _params.insert(std::make_pair(key, val)); }

    // 查询是否存在指定查询字符串
    bool HasParam(const std::string &key) const
    {
        auto it = _params.find(key);
        if (it == _params.end())
            return false;
        return true;
    }

    // 获取指定查询字符串的值
    std::string GetParam(const std::string &key) const
    {
        auto it = _params.find(key);
        if (it == _params.end())
            return "";
        return it->second;
    }

    // 获取正文长度
    size_t BodyLength() const
    {
        // Content-Length: 123\r\n
        bool ret = HasHeader("Content-Length");
        if (ret == false)
            return 0;
        std::string len = GetHeader("Content-Length");
        return std::stol(len);
    }

    // 判断是否是短连接通信
    bool IsShortConn() const
    {
        // Connection字段存在并且为keep-alive则是长连接通信
        if (HasHeader("Connection") && GetHeader("Connection") == "keep-alive")
            return false;
        else
            return true;
    }
};

// 存储HTTP响应信息
// 进行业务处理的同时，让使用者向Response中填充响应要素，完毕后，将其组织成为HTTP响应格式的数据，发送给客户端
/*
要素:
    协议版本 响应状态码 状态码描述信息
    头部字段
    正文
*/
class HttpResponse
{
public:
    // 为了便于外界访问，这里我们同样将成员变量设置为公有
    int _status_code;                                      // 响应状态码
    bool _redirect_flag;                                   // 重定向标志
    std::string _redirect_url;                             // 重定向路径
    std::string _body;                                     // 响应正文
    std::unordered_map<std::string, std::string> _headers; // 响应头部字段
public:
    HttpResponse()
        : _redirect_flag(false), _status_code(200)
    {
    }

    HttpResponse(int status)
        : _redirect_flag(false), _status_code(status)
    {
    }

    void Reset()
    {
        _status_code = 200;
        _redirect_flag = false;
        _body = _redirect_url = "";
        _headers.clear();
    }

    void SetHeader(const std::string &key, const std::string &val) { _headers.insert(std::make_pair(key, val)); }

    bool HasHeader(const std::string &key) const
    {
        auto it = _headers.find(key);
        if (it == _headers.end())
            return false;
        return true;
    }

    std::string GetHeader(const std::string &key) const
    {
        auto it = _headers.find(key);
        if (it == _headers.end())
            return "";
        return it->second;
    }

    void SetBody(const std::string &body, const std::string &type = "text/html")
    {
        _body = body;
        SetHeader("Content-Type", type);
        SetHeader("Content-Length", std::to_string(_body.size()));
    }

    // 设置重定向以及Location头部字段
    void SetRedirect(const std::string &url, int status_code = 302) // 默认设置为临时重定向
    {
        _status_code = status_code;
        _redirect_flag = true;
        _redirect_url = url;
        SetHeader("Location", _redirect_url);
    }

    bool IsShortConn() const
    {
        if (HasHeader("Connection") && GetHeader("Connection") == "keep-alive")
            return false;
        return true;
    }
};

// 记录HTTP请求的接收和处理进度
// 有可能出现接收的数据并不是一条完整的HTTP请求数据，也就是请求的处理需要在多次收到数据后才能处理完成
// 因此在每次处理的时候，就需要将处理的进度记录下来，以便下次从当前进度继续向下处理

// HTTP请求行的最大长度 --- 8KB
#define MAX_LINE_SIZE 8192

typedef enum
{
    RECV_REQLINE, // 接收HTTP请求行
    RECV_HEAD,    // 接收HTTP请求头部
    RECV_BODY,    // 接收HTTP请求正文
    RECV_OVER,    // HTTP请求接收完毕
    RECV_ERROR    // 接收HTTP请求出错
} RecvStage;

class HttpReqCtx
{
    // 上下文的主要作用就是接收和解析缓冲区中的数据，直到从缓冲区中的字节流里面拿出一个完整的HttpRequest
private:
    int _resp_code;        // 响应状态码
    RecvStage _recv_stage; // 当前处于的HTTP请求接收阶段
    HttpRequest _request;  // HTTP请求接收并解析完毕后的请求要素信息
private:
    // 接收HTTP请求行
    void RecvReqLine(Buffer *buf)
    {
        if (_recv_stage != RECV_REQLINE)
            return;

        // 获取一行数据，通过调用buf->GetLineAndMoveIdx()就可以实现。但是要考虑一些特殊的因素
        // 1.如果缓冲区中的数据不足一行怎么办？
        // 2.如果缓冲区中的数据已经有特别特别多了，比如已经10MB多了，但是此时还没有\n字符，那这就是有问题的
        std::string line = buf->GetLineAndMoveIdx();
        if (line.size() == 0)
        {
            if (buf->ReadableSize() > MAX_LINE_SIZE)
            {
                _recv_stage = RECV_ERROR;
                _resp_code = 414; // URI Too Long
                return;
            }
            return; // 说明缓冲区中的数据不足一行，则继续等待新数据的到来，不更新接收状态
        }

        // 虽然接收缓冲区中的数据确实满足了一行，但是这一行的字节大小太大了，这样的情况我也认为他是有问题的
        if (line.size() > MAX_LINE_SIZE)
        {
            _recv_stage = RECV_ERROR;
            _resp_code = 414; // URI Too Long
            return;
        }
        _recv_stage = RECV_HEAD;
        return ParseReqLine(line);
    }

    // 解析HTTP请求行
    void ParseReqLine(std::string &line)
    {
        // 原始请求行：GET /login?user=zhangsan&pass=12 3456 HTTP/1.1\r\n
        // 编码后的请求行：GET %2Flogin?user%3Dzhangsan%26pass%3D12+3456 HTTP/1.1
        // 使用正则表达式进行请求行的正则匹配
        // std::regex::icase：匹配时忽略大小写
        std::smatch matches;
        std::regex exp("(GET|HEAD|POST|PUT|DELETE) ([^?]*)(?:\\?(.*))? (HTTP/1\\.[01])(?:\n|\r\n)?", std::regex::icase);
        bool ret = std::regex_match(line, matches, exp);
        if (ret == false)
        {
            _recv_stage = RECV_ERROR;
            _resp_code = 400; // Bad Request
            return;
        }

        // 字符串为"GET /csdn/login?user=xiaoming&pass=123123 HTTP/1.1"时，regex匹配后的结果如下
        // 0 : GET /csdn/login?user=xiaoming&pass=123123 HTTP/1.1
        // 1 : GET
        // 2 : /csdn/login
        // 3 : user=xiaoming&pass=123123
        // 4 : HTTP/1.1
        _request._method = matches[1];
        // 对于一元操作，将op应用于[first1, last1)范围内的每个元素，并将每个操作返回的值存储在以result开头的范围内
        // 给定的op将被连续调用last1-first1次。op可以是函数指针或函数对象或lambda表达式
        // 如op的一个实现 即将[first1, last1)范围内的每个元素加5，然后依次存储到result中
        // std::transform(first1, last1, result, op_increase);
        std::transform(_request._method.begin(), _request._method.end(), _request._method.begin(), ::toupper);

        // 资源路径的获取，需要进行url解码，但是不需要+转为空格的解码
        _request._path = Util::UrlDecode(matches[2], false);
        _request._version = matches[4];

        // 请求行中查询字符串的解析 查询字符串的格式为 key=val&key=val&key=val&key=val.....
        std::vector<std::string> arr;
        Util::Split(matches[3], "&", &arr);

        for (auto &str : arr)
        {
            std::vector<std::string> tmp;
            Util::Split(str, "=", &tmp);
            if (tmp.size() < 2)
            {
                // tmp的大小如果不足2，那就说明你的这个查询字符串是有问题的
                _recv_stage = RECV_ERROR;
                _resp_code = 400; // Bad Request
                return;
            }

            // W3C文档规定，url编码时，查询字符串parameter中的空格，需要被转换为+号，请求资源路径并没有要求，所以UrlDecode第二个参数为true
            _request.SetParam(Util::UrlDecode(tmp[0], true), Util::UrlDecode(tmp[1], true));
        }
        return;
    }

    // 接收HTTP请求头部
    void RecvHead(Buffer *buf)
    {
        if (_recv_stage != RECV_HEAD)
            return;

        std::string line = buf->GetLineAndMoveIdx();
        // 读到空行为止，则头部字段读取完毕
        while (line != "\n" && line != "\r\n")
        {
            if (line.size() == 0)
            {
                // 如果line长度为0，说明获取一行数据失败
                if (buf->ReadableSize() > MAX_LINE_SIZE)
                {
                    // 如果inbuffer中可读数据长度请求行的最大长度，则说明inbuffer中的请求行数据有问题
                    _recv_stage = RECV_ERROR;
                    _resp_code = 414;
                    return;
                }
                return;
            }

            if (line.size() > MAX_LINE_SIZE)
            {
                _recv_stage = RECV_ERROR;
                _resp_code = 414;
                return;
            }

            ParseHead(line);
            if (_resp_code >= 400)
                return;
            // 继续从buf中获取下一行的数据
            line = buf->GetLineAndMoveIdx();
        }

        _recv_stage = RECV_BODY;
        return;
    }

    // 解析HTTP请求头部
    void ParseHead(std::string line)
    {
        // key: value\r\n
        if (line.back() == '\n')
            line.pop_back();
        if (line.back() == '\r')
            line.pop_back();

        // key: value
        std::vector<std::string> tmp;
        Util::Split(line, ": ", &tmp);
        if (tmp.size() < 2)
        {
            // tmp的大小如果不足2，那就说明你的头部字段是有问题的
            _recv_stage = RECV_ERROR;
            _resp_code = 400; // Bad Request
            return;
        }
        _request.SetHeader(tmp[0], tmp[1]);
        return;
    }

    // 接收HTTP请求正文
    void RecvBody(Buffer *buf)
    {
        if (_recv_stage != RECV_BODY)
            return;
        // 请求头已经解析成功，里面就有正文长度
        size_t len = _request.BodyLength();
        if (len == 0)
        {
            // 没有正文，则请求解析完毕
            _recv_stage = RECV_OVER;
            return;
        }

        // 1. 当前还需要接收多少正文数据
        size_t need_len = len - _request._body.size();
        if (buf->ReadableSize() >= need_len)
        {
            // 2. 如果缓冲区中的数据大小超过正文长度，那取出所需要的大小的正文数据即可，多出来那部分数据就是下一个http请求的信息
            _request._body += buf->ReadAndMoveIdx(need_len);
            _recv_stage = RECV_OVER;
            return;
        }
        // 3. 如果缓冲区中的数据不够当前正文长度的大小，那就说明服务器接收到的数据不足一条完整的数据
        // 那就先把当前这点数据取出来，然后继续等待新的数据到来
        else
        {
            _request._body += buf->ReadAndMoveIdx(buf->ReadableSize());
            return;
            // 这里不更改接收状态_recv_statu，因为我们正文的数据还没有读取完呢，服务器还需要继续等待数据的到来！
            /*
            值得多说一句的是，像客户端发送http请求，那么当这个请求发送到服务器时，其实数据会先被拷贝到socket内核接收缓冲区，然后我们服务器程序内部调用了
            recv system call来将数据拷贝到应用层缓冲区Buffer里面，然而socket的内核recv buffer和send buffer都是有他们的固定空间大小的，他不是无限制增长的
            所以，我们在recv的时候是有可能不能一次性把所有数据都拿完的，因为此时socket的内核接收缓冲区即使被打满了，但他还有可能没有收到完整的http请求数据，
            那就应该在上层程序员recv之后，清空socket内核接收缓冲区后，服务器的socket继续接收剩余的http请求数据，然后服务器的程序员再进行recv将剩余数据拿到Buffer
            里面，这样Buffer里面就又会新增一部分http请求数据，所以如果Buffer里面的正文数据不够完整的话，那我们就再等一等，等等socket内核缓冲区接收到剩余的数据
            ，然后Buffer将内核缓冲区的数据拿上来，拿上来之后，我们就可以继续进行处理http请求正文body的内容了。
            */
        }
    }

public:
    HttpReqCtx()
        : _resp_code(200), _recv_stage(RECV_REQLINE)
    {
    }

    int GetRespCode() { return _resp_code; }

    RecvStage GetRecvStage() { return _recv_stage; }

    HttpRequest &GetWholeReq() { return _request; }

    void ParseHttpReq(Buffer *buf)
    {
        // 状态机：不同的处理阶段，执行不同的函数
        // 注意：case后面不需要加break，因为请求行接收解析完毕后，接下来本来就是要接收解析请求头部的，头部后面又是正文
        switch (_recv_stage)
        {
        case RECV_REQLINE:
            RecvReqLine(buf);
        case RECV_HEAD:
            RecvHead(buf);
        case RECV_BODY:
            RecvBody(buf);
        }
    }

    void Reset()
    {
        _resp_code = 200, _recv_stage = RECV_REQLINE;
        _request.Reset();
    }
};

// 整合封装HTTP服务器模块
class HttpServer
{
private:
    // 业务处理函数
    using Business_Handle = std::function<void(const HttpRequest &, HttpResponse *)>;
    // 特定请求方法的业务处理集合 std::pair<<请求资源路径的正则表达式, 函数指针>>
    // 注意：由于std::regex没有默认的哈希函数，也没有默认的相等比较运算符，所以不能使用unordered_map来存储路由信息
    using Handle_Set = std::vector<std::pair<std::regex, Business_Handle>>;

    Handle_Set _get_route;
    Handle_Set _post_route;
    Handle_Set _put_route;
    Handle_Set _delete_route;
    std::string _webroot; // 静态资源根目录
    TcpServer _server;    // 主从Reactor高并发TCP服务器
private:
    bool IsStaticReq(HttpRequest &req)
    {
        // 1. 必须设置了静态资源的根目录
        if (_webroot.empty())
            return false;
        // 2. 请求方法必须是GET或HEAD，这样获取实体web资源的HTTP请求方法
        if (req._method != "GET" && req._method != "HEAD")
            return false;
        // 3. 请求的资源路径必须是一个有效的合法路径
        if (Util::IsValidPath(req._path) == false)
            return false;
        // 4. 在请求资源路径合法的情况下，请求的资源文件也必须是存在的
        //    有一种请求是比较特殊的 只有目录，请求资源路径只有 "/" 或 "/image/"，这种纯粹的目录情况下我们会在 / 后面追加index.html也就是服务器的首页资源
        std::string path = _webroot + req._path;
        if (req._path.back() == '/')
            path += "index.html";
        // 有可能有人会搞破坏，例如 /image//   转换为  ./wwwroot/image//index.html，对于一个路径里面有两个斜杠，
        // 那他肯定不是一个正常的普通文件，则IsRegular返回false
        if (Util::IsRegular(path) == false)
            return false;
        return true;
    }

    // 处理静态资源的请求，将静态资源文件的数据读取出来，放到resp的body正文中
    void HandleStaticReq(const HttpRequest &req, HttpResponse *resp)
    {
        std::string path = _webroot + req._path;
        if (path.back() == '/')
            path += "index.html";

        // 1. 在判断是否为静态资源时，如果判断成功，则req对象中的_path已经被改为服务器上实际静态资源的路径了，这里直接读取即可
        bool ret = Util::ReadFile(path, &resp->_body);
        if (ret == false)
            return;
        std::string mime = Util::MimeByExt(path);
        resp->SetHeader("Content-Type", mime);
    }

    // 功能性请求业务处理函数
    void HandleDynamicReq(HttpRequest &req, HttpResponse *resp, Handle_Set route_table)
    {
        // 在对应的请求方法的路由表中，查找是否有对应请求的处理函数，如果有则进行调用，如果没有则说明客户端的这种请求在服务器上是找不到的，返回404
        /*处理思想:
        路由表中的key存储的是对应请求资源路径解析时的一个正则表达式，那就说明，只要当前req的url中的资源路径能和路由表中的rgx进行正则匹配，
        那就说明，这个请求在服务器的某个路由表中有对应匹配的处理函数 */
        //         /numbers/12345     <"/numbers/(\\d+)", Handler>
        // regex解释-->C++这一层看到\字符就会进行转义，所以传给regex引擎的就是去掉一个\后的\d+，在regex引擎里面\d+会被解释为匹配数字字符一次或多次
        for (auto &p : route_table)
        {
            std::regex rgx(p.first);
            bool ret = std::regex_match(req._path, req._matches, rgx);
            if (ret)
            {
                p.second(req, resp); // 正则匹配成功，传入请求信息和空的resp，执行对应的业务处理函数
                return;
            }
        }
        resp->_status_code = 404; // not found
    }

    /* HEAD方法与GET方法类似，也是请求从服务器获取资源，服务器的处理机制也是一样的，但服务器不会返回请求的实体数据，只会传回响应头，也就是资源的“元信息”。
    HEAD方法可以看做是GET方法的一个"简化版"或者"轻量版"。因为它的响应头与GET完全相同，所以可以用在很多并不真正需要资源的场合，避免传输 body 数据的浪费。
    比如，想要检查一个文件是否存在，只要发个HEAD请求就可以了，没有必要用GET把整个文件都取下来。再比如，要检查文件是否有最新版本，
    同样也应该用HEAD，服务器会在响应头里把文件的修改时间传回来 */
    // 请求路由与业务处理
    void RouteReq(HttpRequest &req, HttpResponse *resp)
    {
        // 1.对请求进行分类，是一个静态资源请求还是动态功能性请求(GET和HEAD既有可能是静态请求，也有可能是动态请求)
        //   如果既不是静态资源请求，也没有响应的动态功能请求的处理函数，那就返回404，表示请求是不存在的，是未知的，服务器找不到你所请求的资源
        // 2.GET请求也有可能是动态功能性请求，像百度你在进行搜索的时候，其实就是GET请求获取百度服务器中存储的关键字相对应的一系列web资源，而对关键字进行检索
        //   这其实就是一个动态的功能性请求
        if (IsStaticReq(req))
            return HandleStaticReq(req, resp);
        if (req._method == "GET" || req._method == "HEAD")
            return HandleDynamicReq(req, resp, _get_route);
        if (req._method == "POST")
            return HandleDynamicReq(req, resp, _post_route);
        if (req._method == "PUT")
            return HandleDynamicReq(req, resp, _put_route);
        if (req._method == "DELETE")
            return HandleDynamicReq(req, resp, _delete_route);
        resp->_status_code = 405; // Method Not Allowed (请求方法不被服务器允许)
    }

    // 根据HttpResponse中的响应要素信息组织HTTP响应并发送
    void SendResp(const ConnPtr &conn, const HttpRequest &req, HttpResponse *resp)
    {
        // 1. 完善resp的头部字段
        if (req.IsShortConn())
            resp->SetHeader("Connection", "close");
        else
            resp->SetHeader("Connection", "keep-alive");

        if (!resp->_body.empty() && !resp->HasHeader("Content-Length"))
            resp->SetHeader("Content-Length", std::to_string(resp->_body.size()));
        if (!resp->_body.empty() && !resp->HasHeader("Content-Type"))
            resp->SetHeader("Content-Type", "application/octet-stream"); // 二进制流类型
        if (resp->_redirect_flag)
            resp->SetHeader("Location", resp->_redirect_url);

        // 2. 将resp对象中的成员组织成http响应格式的字符串
        std::string resp_str;
        resp_str += req._version + " " + std::to_string(resp->_status_code) + " " + Util::StateDesc(resp->_status_code) + "\r\n";
        for (auto &head : resp->_headers)
        {
            resp_str += head.first + ": " + head.second + "\r\n";
        }
        resp_str += "\r\n";
        resp_str += resp->_body;

        // 3. 将组织好的字符串发送给客户端(其实是发送到conn的_out_buffer里面，实际的发送还要等待事件驱动处理)
        conn->Send(resp_str.c_str(), resp_str.size());
    }

    // 连接建立成功回调函数 -- 由HttpServer模块设置给TcpServer模块，完成上下文的设置
    void OnEstablished(const ConnPtr &conn)
    {
        // 为刚刚建立好的连接设置一个HttpReqCtx类型的上下文信息，则conn的any容器中就会存储HttpReqCtx类型的请求上下文信息
        conn->SetContext(HttpReqCtx());
        DLOG("new connection: %p", conn.get());
    }

    // 错误处理函数
    void OnError(HttpResponse *resp)
    {
        // 组织一个错误页面
        std::string body;
        body += "<html><head><meta http-equiv='Content-Type' content='text/html;charset=utf-8'></head><body><h1>";
        body += std::to_string(resp->_status_code) + " " + Util::StateDesc(resp->_status_code) + "</h1></body></html>";
        // 将响应页面数据作为正文进行响应
        resp->SetBody(body, "text/html");
    }

    // 收到消息回调函数 -- 由HttpServer模块设置给TcpServer模块，完成业务处理
    void OnMessage(const ConnPtr &conn, Buffer *inbuf)
    {
        // 对于Buffer缓冲区中接收到的数据先进行解析，然后再处理
        while (inbuf->ReadableSize() > 0)
        {
            // 1. 获取http请求上下文，对服务器缓冲区中收到的数据进行解析，解析后获得一个完整的HttpRequest对象
            // GetContext返回的是一个Any对象的指针
            // 我们要的是Any里面保存的数据对象的类型的指针，所以调用一下get接口，注意get是一个模板函数，调用时要指定类型
            HttpReqCtx *context = conn->GetContext()->get<HttpReqCtx>();
            context->ParseHttpReq(inbuf);
            HttpResponse resp(context->GetRespCode());
            // 如果缓冲区中的数据解析出错，那就直接组织一个错误的响应信息进行返回，状态码在解析http请求出错的时候就会设置好
            if (context->GetRespCode() >= 400)
            {
                OnError(&resp);
                SendResp(conn, HttpRequest(), &resp);
                inbuf->Clear();
                // 缓冲区中的数据都解析错误了，说明这些数据都已经不符合http协议了，服务器那就没有必要再对数据进行解析了，直接清空这些非HTTP协议的数据即可
                // 在关闭连接之前，一定要将上下文进行重置，将上下文信息恢复到最初的状态，避免影响后续对缓冲区中数据的解析处理
                context->Reset();
                conn->Shutdown();
                return;
            }

            // 当前请求还没有接受完整，则继续等待新数据到来之后再进行处理
            if (context->GetRecvStage() != RECV_OVER)
                return;

            // ---走到这里，说明缓冲区中的数据已经解析完毕了，那么此时请求上下文信息中就会包含一个完整的HttpRequest对象
            // 只要connection没销毁，那么context就不会销毁，自然whole HttpReq就不会销毁，所以这里可以使用引用返回
            HttpRequest &req = context->GetWholeReq();
            // 2. 对HttpRequest对象进行一个请求路由，去查找这个请求相对应的业务处理函数
            RouteReq(req, &resp);
            // 3. 组织发送HttpResponse
            SendResp(conn, req, &resp);
            // 4. 处理完毕一整条请求过后，需要重置一下当前连接的上下文，这样就不会让之前的上下文影响到下一次连接处理http请求了
            context->Reset();
            // 5. 通过当前http的长短连接来判断是否要关闭当前连接，还是继续进行数据通信和处理
            if (resp.IsShortConn())
            {
                // 如果是短连接，那就只进行一次业务逻辑处理，处理完毕之后就关闭掉连接，然后退出循环，结束OnMessage的函数执行
                conn->Shutdown();
                break;
            }
        }
    }

public:
    HttpServer(uint16_t port, int timeout = DEFAULT_TIMEOUT)
        : _server(port)
    {
        // 默认开启连接的非活跃释放
        _server.OpenInactiveRelease(timeout);
        // 只设置连接建立回调和业务逻辑处理回调
        _server.SetEstablishCbUsr(std::bind(&HttpServer::OnEstablished, this, std::placeholders::_1));
        _server.SetMessageCbUsr(std::bind(&HttpServer::OnMessage, this, std::placeholders::_1, std::placeholders::_2));
    }

    void SetWebroot(const std::string &path)
    {
        bool ret = Util::IsDirectory(path);
        assert(ret == true);
        _webroot = path;
    }

    // 添加路由表 --- 请求资源的正则表达式与对应的业务处理函数
    void AddGetRoute(std::string RegExp, const Business_Handle &f) { _get_route.push_back(std::make_pair(std::regex(RegExp), f)); }
    void AddPostRoute(std::string RegExp, const Business_Handle &f) { _post_route.push_back(std::make_pair(std::regex(RegExp), f)); }
    void AddPutRoute(std::string RegExp, const Business_Handle &f) { _put_route.push_back(std::make_pair(std::regex(RegExp), f)); }
    void AddDeleteRoute(std::string RegExp, const Business_Handle &f) { _delete_route.push_back(std::make_pair(std::regex(RegExp), f)); }
    
    // 设置从属线程池数量
    void SetThreadCount(int count) { _server.SetThreadCount(count); }
    
    // 启动服务器
    void Run() { _server.Run(); }
};